# 拦截复制事件并替换图片url

项目为vue

# 监听粘贴事件

/**
 * 监听粘贴事件,将外链的图片转为我们的
 */
listenPaste () {
  window.addEventListener('paste', this.pasteEventHandle)
  this.$on('hook:beforeDestroy', () => {
    window.removeEventListener('paste', this.pasteEventHandle)
  })
},

# 事件处理

  1. 先用html格式获取,没有则用纯文本text格式
  2. 将width,z-index替换为空,解决样式问题
  3. 有图片就拦截原生事件,替换图片为自己服务器的
  4. 没图片就用原生事件粘贴上去就行
async pasteEventHandle (e) {
  // Prevent the default pasting event and stop bubbling
  let paste = (e.clipboardData || window.clipboardData).getData('text/html')
  if (!paste) {
    paste = (e.clipboardData || window.clipboardData).getData('text')
  }
  // Do something with paste like remove non-UTF-8 characters
  this.pasteInfo = paste.replace(/width|z-index/g, '').replace(/[\\]/g, '')
  if (this.pasteInfo.indexOf('<img') !== -1) {
    e.preventDefault()
    e.stopPropagation()
    this.pasteInfo = await this.replaceImgInHtmlString(this.pasteInfo)
    this.pasteToTarget()
  }
},

# 获取图片并替换

用正则匹配出所有图片src,传给接口换取自己服务器链接

将字符串的图片地址替换掉,这一步写法有点蠢,待优化

实测data-src也得替换,貌似会触发懒加载然后把src又换成外链了

async replaceImgInHtmlString (paste) {
  this.showImgPasteProgress = true
  let arrImg = []
  paste.replace(/<(img) [^>]*src=['"]([^'"]+)[^>]*>/gi, function (match, $1, $2) {
    arrImg.push($2)
  })
  const replaceArrImg = await this.changeSystemUrl(arrImg)
  arrImg.forEach((item, index) => {
    paste = paste.replace(`data-src="${item}"`, `data-src="${replaceArrImg[index]}"`)
    paste = paste.replace(`src="${item}"`, `src="${replaceArrImg[index]}" style="width: 100%!important;height: auto!important;"`)
  })
  return paste
},

# 粘贴

转换图片完成后,获取光标位置,调用一系列api将内容赋值上去即可

/**
 * 粘贴到光标处
 */
pasteToTarget () {
  // Find the cursor location or highlighted area
  const selection = window.getSelection()
  // Cancel the paste operation if the cursor or highlighted area isn't found
  if (!selection.rangeCount) return false
  var div = document.createElement('div')
  div.innerHTML = this.pasteInfo
  // Paste the modified clipboard content where it was intended to go
  selection.getRangeAt(0).insertNode(div)
  this.showImgPasteProgress = false
},

# 进度条(假)

上面已经完成了功能,下面加个动画组件,优化下体验

长这样

就直接粘代码了

<!-- 粘贴图文详情进度条组件 -->
<!-- Create by luozheng on 2020/08/10 -->
<style lang="less" scoped>
  @import "../../assets/less/variable.less";
  .progress-bar-box {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    background: rgba(0,0,0,.5);
    z-index: 100;
    padding: 0.18rem 0.32rem;
    color: #ffffff;
    .progress-bar-item {
      width: 70%;
      height: 0.6rem;
      background: #555;
      border-radius: 0.5rem;
      padding: 0.2rem;
      box-shadow: inset 0 -0.02rem 0.02rem rgba(255, 255, 255, 0.3);
    }
    .progress-item {
      width: 100%;
      height: 0.3rem;
      background: #262626;
      padding: 0.06rem;
      overflow: visible;
      border-radius: 0.4rem;
      border-top: 0.02rem solid #000;
      border-bottom: 0.02rem solid #7992a8;
      margin-top: 0.06rem;
    }
    .progress-bar {
      border-radius: 0.4rem;
      position: relative;
      animation: animate-positive 2s;
    }
    .progress-value {
      display: block;
      padding: 0.06rem 0.14rem;
      font-size: 0.26rem;
      color: #fff;
      border-radius: 0.08rem;
      background: #191919;
      border: 0.02rem solid #000;
      position: absolute;
      top: -0.9rem;
      right: -0.2rem;
    }
    .progress-value:after {
      content: "";
      border-top: 0.2rem solid #191919;
      border-left: 0.2rem solid transparent;
      border-right: 0.2rem solid transparent;
      position: absolute;
      bottom: -0.12rem;
      left: 26%;
    }
    .active {
      animation: reverse progress-bar-stripes 0.40s linear infinite, animate-positive 2s;
    }
    .progress-bar-striped {
      background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
      background-size: 0.9rem 0.9rem;
    }
    @keyframes animate-positive {
      0% {
        width: 0;
      }
    }
    .progress-bar {
      float: left;
      width: 0;
      height: 100%;
      font-size: 0.24rem;
      line-height: 0.4rem;
      color: #fff;
      text-align: center;
      -webkit-box-shadow: inset 0 -0.02rem 0 rgba(0, 0, 0, .15);
      box-shadow: inset 0 -0.02rem 0 rgba(0, 0, 0, .15);
      -webkit-transition: width .6s ease;
      -o-transition: width .6s ease;
      transition: width .6s ease;
    }
    @keyframes progress-bar-stripes {
      from {
        background-position: 0.9rem 0
      }
      to {
        background-position: 0 0
      }
    }
  }
  .paste-btn {
    width: 0.8rem;
    height: 0.8rem;
  }
  .paste-progress {
    padding: 0 0.2rem;
  }
</style>

<template>
  <div class="progress-bar-box _flex">
    <img class="paste-btn" src="//image.greenplayer.cn/share/img/icon/icon_paste.svg" alt="">
    <div class="_flex-item paste-progress">
      <div>图文解析中...</div>
      <div class="progress-item">
        <div class="progress-bar progress-bar-striped active"
             :style="{width: `${progress}%`, backgroundColor: bgColor}">
          <!--        <div class="progress-value">{{progress}}%</div>-->
        </div>
      </div>
    </div>
    <div class="paste-btn _flex" @click="clickMenu">
      <i class="ly-icon-sandian"></i>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'ProgressBar',
    data () {
      return {
        progress: this.startProgress
      }
    },
    props: {
      breakPointList: {
        type: Array,
        default: () => [33, 80, 99]
      },
      bgColor: {
        type: String,
        default: '#3db657'
      },
      startProgress: {
        type: Number,
        default: 0
      }
    },
    mounted () {
      this.animationToPercent(...this.breakPointList)
    },
    methods: {
      clickMenu () {
        this.showConfirm({
          content: '确定取消图文解析吗?',
          confirm: () => {
            this.$emit('cancel')
          }
        })
      },
      animationToPercent (percent, nextPercent1, nextPercent2) {
        window.requestAnimationFrame(() => {
          if (this.progress < percent) {
            this.progress++
            this.animationToPercent(percent, nextPercent1, nextPercent2)
          } else {
            setTimeout(() => {
              percent = nextPercent1
              nextPercent1 = nextPercent2
              nextPercent2 = ''
              if (!percent) return
              this.animationToPercent(percent, nextPercent1, nextPercent2)
            }, 500)
          }
        })
      }
    }
  }
</script>